{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "t0CF6Gvkt_Cw"
   },
   "source": [
    "# MuJoCo basics\n",
    "\n",
    "We begin by defining and loading a simple model:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import mujoco\n",
    "import mediapy as media\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "import time\n",
    "import itertools\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "id": "3KJVqak6xdJa"
   },
   "outputs": [],
   "source": [
    "xml = \"\"\"\n",
    "<mujoco>\n",
    "  <worldbody>\n",
    "    <geom name=\"red_box\" type=\"box\" size=\".2 .2 .2\" rgba=\"1 0 0 1\"/>\n",
    "    <geom name=\"green_sphere\" pos=\".2 .2 .2\" size=\".1\" rgba=\"0 1 0 1\"/>\n",
    "  </worldbody>\n",
    "</mujoco>\n",
    "\"\"\"\n",
    "model = mujoco.MjModel.from_xml_string(xml)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "slhf39lGxvDI"
   },
   "source": [
    "The `xml` string is written in MuJoCo's [MJCF](http://www.mujoco.org/book/modeling.html), which is an [XML](https://en.wikipedia.org/wiki/XML#Key_terminology)-based modeling language.\n",
    "  - The only required element is `<mujoco>`. The smallest valid MJCF model is `<mujoco/>` which is a completely empty model.\n",
    "  - All physical elements live inside the `<worldbody>` which is always the top-level body and constitutes the global origin in Cartesian coordinates.\n",
    "  - We define two geoms in the world named `red_box` and `green_sphere`.\n",
    "  - **Question:** The `red_box` has no position, the `green_sphere` has no type, why is that?\n",
    "    - **Answer:** MJCF attributes have *default values*. The default position is `0 0 0`, the default geom type is `sphere`. The MJCF language is described in the documentation's [XML Reference chapter](https://mujoco.readthedocs.io/en/latest/XMLreference.html).\n",
    "\n",
    "The `from_xml_string()` method invokes the model compiler, which creates a binary `mjModel` instance."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "gf9h_wi9weet"
   },
   "source": [
    "## mjModel\n",
    "\n",
    "MuJoCo's `mjModel`, contains the *model description*, i.e., all quantities which *do not change over time*. The complete description of `mjModel` can be found at the end of the header file [`mjmodel.h`](https://github.com/deepmind/mujoco/blob/main/include/mujoco/mjmodel.h). Note that the header files contain short, useful inline comments, describing each field.\n",
    "\n",
    "Examples of quantities that can be found in `mjModel` are `ngeom`, the number of geoms in the scene and `geom_rgba`, their respective colors:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "id": "F40Pe6DY3Q0g"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.ngeom"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "id": "MOIJG9pzx8cA"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1., 0., 0., 1.],\n",
       "       [0., 1., 0., 1.]], dtype=float32)"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.geom_rgba"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "bzcLjdY23Kvp"
   },
   "source": [
    "## Named access\n",
    "\n",
    "The MuJoCo Python bindings provide convenient [accessors](https://mujoco.readthedocs.io/en/latest/python.html#named-access) using names. Calling the `model.geom()` accessor without a name string generates a convenient error that tells us what the valid names are."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "id": "9AuTwbLFyJxQ"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\"Invalid name ''. Valid names: ['green_sphere', 'red_box']\"\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "  model.geom()\n",
    "except KeyError as e:\n",
    "  print(e)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "qkfLK3h2zrqr"
   },
   "source": [
    "Calling the named accessor without specifying a property will tell us what all the valid properties are:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "id": "9X95TlWnyEEw"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<_MjModelGeomViews\n",
       "  bodyid: array([0], dtype=int32)\n",
       "  conaffinity: array([1], dtype=int32)\n",
       "  condim: array([3], dtype=int32)\n",
       "  contype: array([1], dtype=int32)\n",
       "  dataid: array([-1], dtype=int32)\n",
       "  friction: array([1.e+00, 5.e-03, 1.e-04])\n",
       "  gap: array([0.])\n",
       "  group: array([0], dtype=int32)\n",
       "  id: 1\n",
       "  margin: array([0.])\n",
       "  matid: array([-1], dtype=int32)\n",
       "  name: 'green_sphere'\n",
       "  pos: array([0.2, 0.2, 0.2])\n",
       "  priority: array([0], dtype=int32)\n",
       "  quat: array([1., 0., 0., 0.])\n",
       "  rbound: array([0.1])\n",
       "  rgba: array([0., 1., 0., 1.], dtype=float32)\n",
       "  sameframe: array([0], dtype=uint8)\n",
       "  size: array([0.1, 0. , 0. ])\n",
       "  solimp: array([9.0e-01, 9.5e-01, 1.0e-03, 5.0e-01, 2.0e+00])\n",
       "  solmix: array([1.])\n",
       "  solref: array([0.02, 1.  ])\n",
       "  type: array([2], dtype=int32)\n",
       "  user: array([], dtype=float64)\n",
       ">"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.geom('green_sphere')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "mS9qDLevKsJq"
   },
   "source": [
    "Let's read the `green_sphere`'s rgba values:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "id": "xsBlJAV7zpHb"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0., 1., 0., 1.], dtype=float32)"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.geom('green_sphere').rgba"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "8a8hswjjKyIa"
   },
   "source": [
    "This functionality is a convenience shortcut for MuJoCo's [`mj_name2id`](https://mujoco.readthedocs.io/en/latest/APIreference.html?highlight=mj_name2id#mj-name2id) function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "id": "Ng92hNUoKnVq"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0., 1., 0., 1.], dtype=float32)"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_GEOM, 'green_sphere')\n",
    "model.geom_rgba[id, :]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "5WL_SaJPLl3r"
   },
   "source": [
    "Similarly, the read-only `id` and `name` properties can be used to convert from id to name and back:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "id": "2CbGSmRZeE5p"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "id of \"green_sphere\":  1\n",
      "name of geom 1:  green_sphere\n",
      "name of body 0:  world\n"
     ]
    }
   ],
   "source": [
    "print('id of \"green_sphere\": ', model.geom('green_sphere').id)\n",
    "print('name of geom 1: ', model.geom(1).name)\n",
    "print('name of body 0: ', model.body(0).name)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "3RIizubaL_du"
   },
   "source": [
    "Note that the 0th body is always the `world`. It cannot be renamed.\n",
    "\n",
    "The `id` and `name` attributes are useful in Python comprehensions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "id": "m3MtIE5F1K7s"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['red_box', 'green_sphere']"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[model.geom(i).name for i in range(model.ngeom)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "t5hY0fyXFLcf"
   },
   "source": [
    "## `mjData`\n",
    "`mjData` contains the *state* and quantities that depend on it. The state is made up of time, [generalized](https://en.wikipedia.org/wiki/Generalized_coordinates) positions and generalized velocities. These are respectively `data.time`, `data.qpos` and `data.qvel`. In order to make a new `mjData`, all we need is our `mjModel`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "id": "FV2Hy6m948nr"
   },
   "outputs": [],
   "source": [
    "data = mujoco.MjData(model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "-KmNuvlJ46u0"
   },
   "source": [
    "`mjData` also contains *functions of the state*, for example the Cartesian positions of objects in the world frame. The (x, y, z) positions of our two geoms are in `data.geom_xpos`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "id": "CPwDcAQ0-uUE"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[0. 0. 0.]\n",
      " [0. 0. 0.]]\n"
     ]
    }
   ],
   "source": [
    "print(data.geom_xpos)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Sjst5xGXX3sr"
   },
   "source": [
    "Wait, why are both of our geoms at the origin? Didn't we offset the green sphere? The answer is that derived quantities in `mjData` need to be explicitly propagated (see [below](#scrollTo=QY1gpms1HXeN)). In our case, the minimal required function is [`mj_kinematics`](https://mujoco.readthedocs.io/en/latest/APIreference.html#mj-kinematics), which computes global Cartesian poses for all objects (excluding cameras and lights)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "id": "tfe0YeZRYNTr"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "raw access:\n",
      " [[0.  0.  0. ]\n",
      " [0.2 0.2 0.2]]\n",
      "\n",
      "named access:\n",
      " [0.2 0.2 0.2]\n"
     ]
    }
   ],
   "source": [
    "mujoco.mj_kinematics(model, data)\n",
    "print('raw access:\\n', data.geom_xpos)\n",
    "\n",
    "# MjData also supports named access:\n",
    "print('\\nnamed access:\\n', data.geom('green_sphere').xpos)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "eU7uWNsTwmcZ"
   },
   "source": [
    "# Basic rendering, simulation, and animation\n",
    "\n",
    "In order to render we'll need to instantiate a `Renderer` object and call its `render` method.\n",
    "\n",
    "We'll also reload our model to make the colab's sections independent."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "id": "xK3c0-UDxMrN"
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table class=\"show_images\" style=\"border-spacing:0px;\"><tr><td style=\"padding:1px;\"><img width=\"320\" height=\"240\" style=\"image-rendering:auto; object-fit:cover;\" src=\"\"/></td></tr></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "xml = \"\"\"\n",
    "<mujoco>\n",
    "  <worldbody>\n",
    "    <light name=\"top\" pos=\"0 0 1\"/>\n",
    "    <geom name=\"red_box\" type=\"box\" size=\".2 .2 .2\" rgba=\"1 0 0 1\"/>\n",
    "    <geom name=\"green_sphere\" pos=\".2 .2 .2\" size=\".1\" rgba=\"0 1 0 1\"/>\n",
    "  </worldbody>\n",
    "</mujoco>\n",
    "\"\"\"\n",
    "#\n",
    "# Make model and data\n",
    "model = mujoco.MjModel.from_xml_string(xml)\n",
    "data = mujoco.MjData(model)\n",
    "\n",
    "# Make renderer, render and show the pixels\n",
    "renderer = mujoco.Renderer(model)\n",
    "media.show_image(renderer.render())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ZkFSHeYGxlT5"
   },
   "source": [
    "Hmmm, why the black pixels?\n",
    "\n",
    "**Answer:** For the same reason as above, we first need to propagate the values in `mjData`. This time we'll call [`mj_forward`](https://mujoco.readthedocs.io/en/latest/APIreference.html#mj-forward), which invokes the entire pipeline up to the computation of accelerations i.e., it computes $\\dot x = f(x)$, where $x$ is the state. This function does more than we actually need, but unless we care about saving computation time, it's good practice to call `mj_forward` since then we know we are not missing anything.\n",
    "\n",
    "We also need to update the `mjvScene` which is an object held by the renderer describing the visual scene. We'll later see that the scene can include visual objects which are not part of the physical model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "id": "pvh47r97huS4"
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table class=\"show_images\" style=\"border-spacing:0px;\"><tr><td style=\"padding:1px;\"><img width=\"320\" height=\"240\" style=\"image-rendering:auto; object-fit:cover;\" src=\"\"/></td></tr></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "mujoco.mj_forward(model, data)\n",
    "renderer.update_scene(data)\n",
    "\n",
    "media.show_image(renderer.render())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "-P95E-QHizQq"
   },
   "source": [
    "# Simulation\n",
    "\n",
    "Now let's simulate and make a video. We'll use MuJoCo's main high level function `mj_step`, which steps the state $x_{t+h} = f(x_t)$.\n",
    "\n",
    "Note that in the code block below we are *not* rendering after each call to `mj_step`. This is because the default timestep is 2ms, and we want a 60fps video, not 500fps."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "id": "NdVnHOYisiKl"
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<table class=\"show_videos\" style=\"border-spacing:0px;\"><tr><td style=\"padding:1px;\"><video controls width=\"320\" height=\"240\" style=\"object-fit:cover;\" loop autoplay muted>\n",
       "      <source src=\"data:video/mp4;base64,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AAEAAAAAAAAAAQAAAQAAAAABAAAFAAAAAAEAAAIAAAAAAQAAAAAAAAABAAABAAAAAAEAAAUAAAAAAQAAAgAAAAABAAAAAAAAAAEAAAEAAAAAAQAABQAAAAABAAACAAAAAAEAAAAAAAAAAQAAAQAAAAABAAAFAAAAAAEAAAIAAAAAAQAAAAAAAAABAAABAAAAAAEAAAUAAAAAAQAAAgAAAAABAAAAAAAAAAEAAAEAAAAAAQAABQAAAAABAAACAAAAAAEAAAAAAAAAAQAAAQAAAAABAAAFAAAAAAEAAAIAAAAAAQAAAAAAAAABAAABAAAAAAEAAAUAAAAAAQAAAgAAAAABAAAAAAAAAAEAAAEAAAAAAQAABQAAAAABAAACAAAAAAEAAAAAAAAAAQAAAQAAAAABAAAFAAAAAAEAAAIAAAAAAQAAAAAAAAABAAABAAAAAAEAAAUAAAAAAQAAAgAAAAABAAAAAAAAAAEAAAEAAAAAAQAABQAAAAABAAACAAAAAAEAAAAAAAAAAQAAAQAAAAABAAAFAAAAAAEAAAIAAAAAAQAAAAAAAAABAAABAAAAAAEAAAUAAAAAAQAAAgAAAAABAAAAAAAAAAEAAAEAAAAAAQAABQAAAAABAAACAAAAAAEAAAAAAAAAAQAAAQAAAAABAAAFAAAAAAEAAAIAAAAAAQAAAAAAAAABAAABAAAAAAEAAAUAAAAAAQAAAgAAAAABAAAAAAAAAAEAAAEAAAAAAQAABQAAAAABAAACAAAAAAEAAAAAAAAAAQAAAQAAAAABAAAFAAAAAAEAAAIAAAAAAQAAAAAAAAABAAABAAAAAAEAAAUAAAAAAQAAAgAAAAABAAAAAAAAAAEAAAEAAAAAAQAABQAAAAABAAACAAAAAAEAAAAAAAAAAQAAAQAAAAABAAAFAAAAAAEAAAIAAAAAAQAAAAAAAAABAAABAAAAAAEAAAUAAAAAAQAAAgAAAAABAAAAAAAAAAEAAAEAAAAAAQAABQAAAAABAAACAAAAAAEAAAAAAAAAAQAAAQAAAAABAAAFAAAAAAEAAAIAAAAAAQAAAAAAAAABAAABAAAAAAEAAAUAAAAAAQAAAgAAAAABAAAAAAAAAAEAAAEAAAAAAQAABQAAAAABAAACAAAAAAEAAAAAAAAAAQAAAQAAAAABAAAFAAAAAAEAAAIAAAAAAQAAAAAAAAABAAABAAAAAAEAAAUAAAAAAQAAAgAAAAABAAAAAAAAAAEAAAEAAAAAAQAABQAAAAABAAACAAAAAAEAAAAAAAAAAQAAAQAAAAABAAAFAAAAAAEAAAIAAAAAAQAAAAAAAAABAAABAAAAAAEAAAUAAAAAAQAAAgAAAAABAAAAAAAAAAEAAAEAAAAAAQAABQAAAAABAAACAAAAAAEAAAAAAAAAAQAAAQAAAAABAAAFAAAAAAEAAAIAAAAAAQAAAAAAAAABAAABAAAAAAEAAAUAAAAAAQAAAgAAAAABAAAAAAAAAAEAAAEAAAAAAQAABQAAAAABAAACAAAAAAEAAAAAAAAAAQAAAQAAAAABAAAFAAAAAAEAAAIAAAAAAQAAAAAAAAABAAABAAAAAAEAAAUAAAAAAQAAAgAAAAABAAAAAAAAAAEAAAEAAAAAAQAABQAAAAABAAACAAAAAAEAAAAAAAAAAQAAAQAAAAABAAAFAAAAAAEAAAIAAAAAAQAAAAAAAAABAAABAAAAAAEAAAUAAAAAAQAAAgAAAAABAAAAAAAAAAEAAAEAAAAAAQAABQAAAAABAAACAAAAAAEAAAAAAAAAAQAAAQAAAAABAAAFAAAAAAEAAAIAAAAAAQAAAAAAAAABAAABAAAAAAEAAAUAAAAAAQAAAgAAAAABAAAAAAAAAAEAAAEAAAAAAQAABQAAAAABAAACAAAAAAEAAAAAAAAAAQAAAQAAAAABAAAFAAAAAAEAAAIAAAAAAQAAAAAAAAABAAABAAAAAAEAAAUAAAAAAQAAAgAAAAABAAAAAAAAAAEAAAEAAAAAAQAABQAAAAABAAACAAAAAAEAAAAAAAAAAQAAAQAAAAABAAAFAAAAAAEAAAIAAAAAAQAAAAAAAAABAAABAAAAAAEAAAUAAAAAAQAAAgAAAAABAAAAAAAAAAEAAAEAAAAAAQAABQAAAAABAAACAAAAAAEAAAAAAAAAAQAAAQAAAAABAAAFAAAAAAEAAAIAAAAAAQAAAAAAAAABAAABAAAAABxzdHNjAAAAAAAAAAEAAAABAAAA5QAAAAEAAAOoc3RzegAAAAAAAAAAAAAA5QAAC3EAAAAcAAAADAAAAA0AAAANAAAAFAAAAA4AAAANAAAADQAAABQAAAAOAAAADQAAAA0AAAAUAAAADgAAAA0AAAANAAAAFAAAAA4AAAANAAAADQAAABQAAAAOAAAADQAAAA0AAAAUAAAADgAAAA0AAAANAAAAFAAAAA4AAAANAAAADQAAABQAAAAOAAAADQAAAA0AAAAUAAAADgAAAA0AAAANAAAAFAAAAA4AAAANAAAADQAAABQAAAAOAAAADQAAAA0AAAAUAAAADgAAAA0AAAANAAAAFAAAAA4AAAANAAAADQAAABQAAAAOAAAADQAAAA0AAAAUAAAADgAAAA0AAAANAAAAFAAAAA4AAAANAAAADQAAABQAAAAOAAAADQAAAA0AAAAUAAAADgAAAA0AAAANAAAAFAAAAA4AAAANAAAADQAAABQAAAAOAAAADQAAAA0AAAAUAAAADgAAAA0AAAANAAAAFAAAAA4AAAANAAAADQAAABQAAAAOAAAADQAAAA0AAAAUAAAADgAAAA0AAAANAAAAFAAAAA4AAAANAAAADQAAABQAAAAOAAAADQAAAA0AAAAUAAAADgAAAA0AAAANAAAAFAAAAA4AAAANAAAADQAAABQAAAAOAAAADQAAAA0AAAAUAAAADgAAAA0AAAANAAAAFAAAAA4AAAANAAAADQAAABQAAAAOAAAADQAAAA0AAAAUAAAADgAAAA0AAAANAAAAFAAAAA4AAAANAAAADQAAABQAAAAOAAAADQAAAA0AAAAUAAAADgAAAA0AAAANAAAAFAAAAA4AAAANAAAADQAAABQAAAAOAAAADQAAAA0AAAAUAAAADgAAAA0AAAANAAAAFAAAAA4AAAANAAAADQAAABQAAAAOAAAADQAAAA0AAAAUAAAADgAAAA0AAAANAAAAFAAAAA4AAAANAAAADQAAABQAAAAOAAAADQAAAA0AAAAUAAAADgAAAA0AAAANAAAAFAAAAA4AAAANAAAADQAAABQAAAAOAAAADQAAAA0AAAAUAAAADgAAAA0AAAANAAAAFAAAAA4AAAANAAAADQAAABQAAAAOAAAADQAAAA0AAAAUAAAADgAAAA0AAAANAAAAFAAAAA4AAAANAAAADQAAABQAAAAOAAAADQAAAA0AAAAUAAAADgAAAA0AAAANAAAAFAAAAA4AAAANAAAADQAAABQAAAAOAAAADQAAAA0AAAAUc3RjbwAAAAAAAAABAAAAMAAAAGJ1ZHRhAAAAWm1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXJhcHBsAAAAAAAAAAAAAAAALWlsc3QAAAAlqXRvbwAAAB1kYXRhAAAAAQAAAABMYXZmNTguNzYuMTAw\" type=\"video/mp4\"/>\n",
       "      This browser does not support the video tag.\n",
       "      </video></td></tr></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "duration = 3.8  # (seconds)\n",
    "framerate = 60  # (Hz)\n",
    "\n",
    "# Simulate and display video.\n",
    "frames = []\n",
    "mujoco.mj_resetData(model, data)  # Reset state and time.\n",
    "while data.time < duration:\n",
    "  mujoco.mj_step(model, data)\n",
    "  if len(frames) < data.time * framerate:\n",
    "    renderer.update_scene(data)\n",
    "    pixels = renderer.render()\n",
    "    frames.append(pixels)\n",
    "media.show_video(frames, fps=framerate)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "-P95E-QHizQq"
   },
   "source": [
    "But isn't anything moving?"
   ]
  }
 ],
 "metadata": {
  "accelerator": "GPU",
  "colab": {
   "collapsed_sections": [
    "-re3Szx-1Ias"
   ],
   "private_outputs": true,
   "provenance": [],
   "toc_visible": true
  },
  "gpuClass": "premium",
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
